{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "executionInfo": {
     "elapsed": 5726,
     "status": "ok",
     "timestamp": 1643739988450,
     "user": {
      "displayName": "WENHAO ZHANG",
      "photoUrl": "https://lh3.googleusercontent.com/a/default-user=s64",
      "userId": "06341037762315879082"
     },
     "user_tz": 480
    },
    "id": "uuYipxE_vyFO"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 Physical GPUs, 1 Logical GPUs\n"
     ]
    }
   ],
   "source": [
    "#load data\n",
    "import os\n",
    "import numpy as np\n",
    "import h5py\n",
    "from pathlib import Path\n",
    "from sklearn.utils import shuffle\n",
    "\n",
    "#visualization\n",
    "import matplotlib.pyplot as plt\n",
    "from sklearn.metrics import plot_confusion_matrix\n",
    "from sklearn.metrics import ConfusionMatrixDisplay\n",
    "import pprint\n",
    "\n",
    "# deep learning library\n",
    "from tensorflow import keras\n",
    "from tensorflow.keras import layers\n",
    "from tensorflow.keras.models import load_model\n",
    "\n",
    "# test model performance\n",
    "from sklearn.metrics import confusion_matrix\n",
    "from sklearn.metrics import classification_report\n",
    "import numpy as np\n",
    "from tensorflow.keras.callbacks import ModelCheckpoint\n",
    "from tensorflow import keras\n",
    "\n",
    "# limit gpu memory\n",
    "import tensorflow as tf\n",
    "gpus = tf.config.experimental.list_physical_devices('GPU')\n",
    "if gpus:\n",
    "    try:\n",
    "        # Currently, memory growth needs to be the same across GPUs\n",
    "        for gpu in gpus:\n",
    "            tf.config.experimental.set_memory_growth(gpu, True)\n",
    "        logical_gpus = tf.config.experimental.list_logical_devices('GPU')\n",
    "        print(len(gpus), \"Physical GPUs,\", len(logical_gpus), \"Logical GPUs\")\n",
    "    except RuntimeError as e:\n",
    "        # Memory growth must be set before GPUs have been initialized\n",
    "        print(e)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "OinZmwdrvyFP"
   },
   "source": [
    "## 1.1. Directory set up"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "id": "HEbNv-6qvyFP"
   },
   "outputs": [],
   "source": [
    "raw_data_path = Path('./data/mit-bih-arrhythmia-database-1.0.0')\n",
    "# main directory that store data\n",
    "data_dir = Path('./data')\n",
    "os.makedirs(data_dir, exist_ok=True)\n",
    "\n",
    "# directory that store original MIT-BIH data\n",
    "\n",
    "mit_arrh_dir = raw_data_path\n",
    "os.makedirs(mit_arrh_dir, exist_ok=True)\n",
    "\n",
    "# directory that store processed data\n",
    "process_dir = data_dir / 'processed_data'\n",
    "temp_dir = process_dir / 'temp'\n",
    "os.makedirs(process_dir, exist_ok=True)\n",
    "\n",
    "raw_data_dir = process_dir\n",
    "os.makedirs(raw_data_dir, exist_ok=True)\n",
    "\n",
    "\n",
    "\n",
    "## Use this for interpatient\n",
    "mit_processed_dir = process_dir / 'ECG_MIT-BIH_processed_data_interpatient_125Hz.h5'\n",
    "\n",
    "## Use this for intrapatient\n",
    "mit_processed_dir = process_dir / 'ECG_MIT-BIH_processed_data_intrapatient_125Hz.h5'\n",
    "\n",
    "# directory that store models data\n",
    "model_dir = Path(\"./model\")\n",
    "os.makedirs(model_dir, exist_ok=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "0SQYMzLzvyFQ"
   },
   "source": [
    "## 1.2 Load train and test dataset\n",
    "- Train and test set is splited and stratified with ratio of 4:1\n",
    "- Dataformat: Each entry contain 1000 sample of of ECG data zero padded representing 2 consecutive heart beat starting from the peak of the first one\n",
    "- Label: 5 classes (F, N, Q, S, V)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "id": "LxhsAjckvyFR",
    "outputId": "a55da6ea-ee21-4bf2-d8b2-23e894e31aa9"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train set length: 50999 (50999, 1024)\n",
      "Test set length: 49690 (49690, 1024)\n"
     ]
    }
   ],
   "source": [
    "## read data from hdf5 file\n",
    "with h5py.File(mit_processed_dir, 'r') as file:\n",
    "    data_train = file.get('train_data')[:]\n",
    "    label_train = file.get('train_labels')[:]\n",
    "    data_test = file.get('test_data')[:]\n",
    "    label_test_no_encoding = file.get('test_labels')[:]\n",
    "\n",
    "## shuffle data\n",
    "data_train, label_train = shuffle(data_train, label_train)\n",
    "\n",
    "## resize data into array shape of (1000,1) to be fed into keras learning module\n",
    "data_train = np.array(data_train)\n",
    "# data_train = np.resize(data_train,(len(data_train), 1000, 1))\n",
    "\n",
    "data_test = np.array(data_test)\n",
    "# data_test = np.resize(data_test,(len(data_test), 1000, 1))\n",
    "\n",
    "# Convert labels to categorical one-hot encoding for multiclass training\n",
    "label_test = keras.utils.to_categorical(label_test_no_encoding, num_classes=5)\n",
    "label_train = keras.utils.to_categorical(label_train, num_classes=5)\n",
    "\n",
    "print(\"Train set length:\", len(data_train), data_train.shape)\n",
    "print(\"Test set length:\", len(data_test), data_test.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "5jJz5X3vvyFR"
   },
   "source": [
    "## 1.3 Visualize sample data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "id": "NfHpWGMTvyFS",
    "outputId": "63cc1f0c-58c1-4269-d7ba-b62d0b01a550"
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 1080x360 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 432x288 with 0 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "## plot a sample train set signal\n",
    "train_ecg_sample = data_train[1024]\n",
    "train_time_in_second = np.arange(len(train_ecg_sample)) / 125.\n",
    "\n",
    "plt.figure(figsize=(15,5))\n",
    "plt.plot(train_time_in_second, train_ecg_sample, c='b', label='125 Hz')\n",
    "plt.xlabel('time in second')\n",
    "plt.ylabel('ECG value in mV')\n",
    "plt.title('Train ECG data for one beat')\n",
    "plt.legend()\n",
    "plt.show()\n",
    "plt.clf()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "id": "sOOT9e2MvyFT",
    "outputId": "6fad669f-35c9-4d09-9260-85fa5b295f47"
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 1080x360 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 432x288 with 0 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "## plot a sample test set signal\n",
    "test_ecg_sample = data_test[1024]\n",
    "test_time_in_second = np.arange(len(test_ecg_sample)) / 125.\n",
    "\n",
    "plt.figure(figsize=(15,5))\n",
    "plt.plot(test_time_in_second, test_ecg_sample, c='b', label='125 Hz')\n",
    "plt.xlabel('time in second')\n",
    "plt.ylabel('ECG value in mV')\n",
    "plt.title('Test ECG data for one beat')\n",
    "plt.legend()\n",
    "plt.show()\n",
    "plt.clf()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "VmoClqEevyFV"
   },
   "source": [
    "# 2 Classification"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2.1 Define model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def prepare_model(config, weight_path=None, suffix=''):\n",
    "    input_shape = config['input_size']\n",
    "    conv_filter_size = config['conv_filter_size']\n",
    "    conv_filter_number = config['conv_filter_number']\n",
    "    layer_num = config['layer_num']\n",
    "    batch_norm = config['batch_norm']\n",
    "    dropout_rate = config['dropout_rate']\n",
    "    pooling_size = config['pooling_size']\n",
    "    pooling_strides = config['pooling_strides']\n",
    "    fc_filter_number = config['fc_filter_number']\n",
    "    fc_layer_num = config['fc_layer_num']\n",
    "    output_size = config['output_size']\n",
    "    kernel_regularizer = config['kernel_regularizer']\n",
    "    \n",
    "    if weight_path is None:\n",
    "        trainable = True\n",
    "    else:\n",
    "        trainable = False\n",
    "        \n",
    "    if kernel_regularizer is not None:\n",
    "        kernel_regularizer = keras.regularizers.l2(kernel_regularizer)\n",
    "    \n",
    "    inputs = keras.Input(shape=input_shape)\n",
    "    start_outputs = layers.Conv1D(conv_filter_number, conv_filter_size, padding='same', \n",
    "                                  kernel_regularizer=kernel_regularizer,\n",
    "                                  name='start_conv', trainable=trainable)(inputs)\n",
    "    \n",
    "    prev_output = start_outputs\n",
    "    for i in range(layer_num):\n",
    "        conv1 = layers.Conv1D(conv_filter_number, conv_filter_size,\n",
    "                              kernel_regularizer=kernel_regularizer,\n",
    "                              padding='same', name='r{}_conv_{}'.format(i, 0), trainable=trainable)(prev_output)\n",
    "        act1 = layers.Activation('relu')(conv1)\n",
    "\n",
    "        if batch_norm:\n",
    "            batch1 = layers.BatchNormalization(name='r{}_batch_{}'.format(i, 0), trainable=trainable)(act1)\n",
    "        else:\n",
    "            batch1 = conv1\n",
    "            \n",
    "        conv2 = layers.Conv1D(conv_filter_number, conv_filter_size, padding='same',\n",
    "                              kernel_regularizer=kernel_regularizer,\n",
    "                              name='r{}_conv_{}'.format(i, 1), trainable=trainable)(batch1)\n",
    "        merge = layers.add([conv2, prev_output])\n",
    "        act2 = layers.Activation('relu')(merge)\n",
    "        \n",
    "        if batch_norm:\n",
    "            batch2 = layers.BatchNormalization(name='r{}_batch_{}'.format(i, 1), trainable=trainable)(act2)\n",
    "        else:\n",
    "            batch2 = act2\n",
    "            \n",
    "        prev_output = layers.MaxPooling1D(pool_size=pooling_size, strides=pooling_strides)(batch2)\n",
    "        \n",
    "    flatten = layers.Flatten()(prev_output)\n",
    "    \n",
    "    prev_output = flatten\n",
    "    for i in range(fc_layer_num):\n",
    "        pred_x0 = layers.Dense(fc_filter_number,\n",
    "                               kernel_regularizer=kernel_regularizer,\n",
    "                               name='{}pred_x_{}'.format(suffix, i))(prev_output)\n",
    "        pred_a0 = layers.Activation('relu')(pred_x0)\n",
    "\n",
    "        if batch_norm:\n",
    "            pred_b0 = layers.BatchNormalization(name='{}p1_batch_{}'.format(suffix, i))(pred_a0)\n",
    "        else:\n",
    "            pred_b0 = pred_a0\n",
    "\n",
    "        if dropout_rate is not None:\n",
    "            pred_d0 = layers.Dropout(dropout_rate)(pred_b0)\n",
    "        else:\n",
    "            pred_d0 = pred_b0\n",
    "        \n",
    "        prev_output = pred_d0\n",
    "    \n",
    "    pred_outputs = layers.Dense(output_size, activation='softmax', name='output')(prev_output)\n",
    "    \n",
    "    full_model = keras.Model(inputs=inputs, outputs=pred_outputs, name='{}full_model'.format(suffix))\n",
    "    \n",
    "    if weight_path is not None:\n",
    "        full_model.load_weights(weight_path, by_name=True)\n",
    "    \n",
    "    full_model.compile(optimizer='adam',\n",
    "              loss='categorical_crossentropy',\n",
    "              metrics=['categorical_accuracy'])\n",
    "    \n",
    "    return full_model\n",
    "\n",
    "model_config = {\n",
    "    \"input_size\": (1024,1),\n",
    "    \"conv_filter_number\": 32,\n",
    "    \"conv_filter_size\":5,\n",
    "    \"batch_norm\": True,\n",
    "    \"dropout_rate\": 0.2,\n",
    "    \"layer_num\": 5,\n",
    "    \"pooling_size\": 5,\n",
    "    \"pooling_strides\": 2,\n",
    "    \"fc_filter_number\": 32,\n",
    "    \"fc_layer_num\": 2,\n",
    "    \"output_size\": 5,\n",
    "    \"kernel_regularizer\": None\n",
    "}\n",
    "\n",
    "model_model = prepare_model(model_config, suffix='CNN')\n",
    "print(model_model.summary())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "315hua91vyFV"
   },
   "source": [
    "## 2.2 Train model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "id": "VE-7IgaYvyFV"
   },
   "outputs": [],
   "source": [
    "## train model with 0.2 spit for validation\n",
    "from tensorflow.keras.callbacks import ModelCheckpoint\n",
    "import matplotlib.pyplot as plt\n",
    "import pandas as pd\n",
    "\n",
    "def train_model(model, data_train, label_train, data_test, label_test, epochs, batch_size, suffix='', save_dir=None):\n",
    "    \"\"\"\n",
    "    This function will train the model on the train dataset\n",
    "    Validation split is set to 20%\n",
    "    \"\"\"\n",
    "    if save_dir is not None:\n",
    "        mc = ModelCheckpoint(str(save_dir / (suffix + '_model_{epoch:04d}.h5')),\n",
    "                              save_freq='epoch')\n",
    "        history = model.fit(data_train, label_train, validation_data=(data_test, label_test),\n",
    "                            epochs=epochs, batch_size=batch_size, callbacks=[mc], shuffle=True)\n",
    "        \n",
    "    else:\n",
    "        history = model.fit(data_train, label_train, validation_split=0.2, epochs=epochs, batch_size=batch_size)\n",
    "    return history\n",
    "\n",
    "\n",
    "\n",
    "def record_train_history(history, save_dir, name):\n",
    "    plt.figure(figsize=(10,5))\n",
    "    step = np.arange(1, len(history.history['loss']) + 1)\n",
    "    plt.plot(history.history['categorical_accuracy'])\n",
    "    plt.plot(history.history['val_categorical_accuracy'])\n",
    "    plt.plot(history.history['loss'])\n",
    "    plt.plot(history.history['val_loss'])\n",
    "    plt.title('Model result')\n",
    "    plt.xlabel('Epoch')\n",
    "    plt.xticks(step)\n",
    "    plt.legend(['Train', 'Test', 'Loss', 'Val_loss'], loc='upper left')\n",
    "    plt.savefig(save_dir / '{}.png'.format(name))\n",
    "    plt.clf()\n",
    "    plt.close()\n",
    "\n",
    "    d = {'epochs': step,\n",
    "         'categorical_accuracy': history.history['categorical_accuracy'],\n",
    "         'val_categorical_accuracy': history.history['val_categorical_accuracy'],\n",
    "         'loss': history.history['loss'],\n",
    "         'val_lost': history.history['val_loss']\n",
    "        }\n",
    "    df = pd.DataFrame(data=d)\n",
    "    df.to_csv(save_dir / '{}.csv'.format(name), index=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "id": "8PCBn0X2vyFW"
   },
   "outputs": [],
   "source": [
    "model_dir = Path(\"./\") / \"model\"\n",
    "result_dir = Path(\"./\") / \"result\"\n",
    "\n",
    "os.makedirs(model_dir, exist_ok = True)\n",
    "os.makedirs(result_dir, exist_ok = True)\n",
    "\n",
    "model_config = {\n",
    "    \"input_size\": (1024,1),\n",
    "    \"conv_filter_number\": 32,\n",
    "    \"conv_filter_size\":5,\n",
    "    \"batch_norm\": True,\n",
    "    \"dropout_rate\": 0.2,\n",
    "    \"layer_num\": 5,\n",
    "    \"pooling_size\": 5,\n",
    "    \"pooling_strides\": 2,\n",
    "    \"fc_filter_number\": 32,\n",
    "    \"fc_layer_num\": 2,\n",
    "    \"output_size\": 5,\n",
    "    \"kernel_regularizer\": None\n",
    "}\n",
    "\n",
    "model_model = prepare_model(model_config, suffix='CNN')\n",
    "\n",
    "history = train_model(model, data_train, label_train, data_test, label_test,\n",
    "                              epochs, batch_size, suffix=exp_name, save_dir=model_dir)\n",
    "record_train_history(history, result_dir, \"CNN\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "4X7_T4XSvyFX"
   },
   "source": [
    "## 2.3 Test model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "FwAMO8nvvyFY"
   },
   "outputs": [],
   "source": [
    "## test model\n",
    "\n",
    "def test_model(model, data_test, label_test):\n",
    "    \"\"\"\n",
    "    This function will run test of the model on the test dataset and return \n",
    "        - classification report string (for display purpose)\n",
    "        - dictionary of classification report (for query purpose)\n",
    "        - confusion matrix\n",
    "    \"\"\"\n",
    "    Y_pred = model.predict(data_test)\n",
    "    y_pred = np.argmax(Y_pred, axis=1)\n",
    "    class_labels = np.argmax(label_test, axis=1)\n",
    "    target_names = ['N', 'S', 'V', 'F', 'Q']\n",
    "    \n",
    "    report = classification_report(class_labels, y_pred, target_names=target_names, digits=3)\n",
    "    report_dict = classification_report(class_labels, y_pred, target_names=target_names, output_dict=True)\n",
    "    c_matrix = confusion_matrix(class_labels, y_pred)\n",
    "    return report, report_dict, c_matrix\n",
    "\n",
    "test_config = {\n",
    "    \"input_size\": (1000,1),\n",
    "    \"conv_filter_number\": 32,\n",
    "    \"conv_filter_size\":5,\n",
    "    \"batch_norm\": True,\n",
    "    \"dropout_rate\": 0.2,\n",
    "    \"layer_num\": 5,\n",
    "    \"pooling_size\": 5,\n",
    "    \"pooling_strides\": 2,\n",
    "    \"fc_filter_number\": 32,\n",
    "    \"fc_layer_num\": 2,\n",
    "    \"output_size\": 5,\n",
    "    \"kernel_regularizer\": None\n",
    "}\n",
    "\n",
    "trained_model = keras.models.load_model(Path(\"./model/interpatient.h5\"))\n",
    "\n",
    "report, report_dict, c_matrix = test_model(trained_model, data_test, label_test)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "id": "ohqokWCcvyFY",
    "outputId": "46927827-4a67-4a86-de2a-45b47b2005ff"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Unbalanced dataset training result:\n",
      "               precision    recall  f1-score   support\n",
      "\n",
      "           N      0.944     0.845     0.892     44238\n",
      "           S      0.000     0.000     0.000      1836\n",
      "           V      0.309     0.924     0.464      3221\n",
      "           F      0.010     0.013     0.012       388\n",
      "           Q      0.000     0.000     0.000         7\n",
      "\n",
      "    accuracy                          0.812     49690\n",
      "   macro avg      0.253     0.356     0.273     49690\n",
      "weighted avg      0.861     0.812     0.824     49690\n",
      "\n"
     ]
    }
   ],
   "source": [
    "## support is number of instance in a class\n",
    "print('Dataset training result:\\n', report)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "id": "RwiSoZwdvyFY",
    "outputId": "4d805e48-0a8a-47f9-c7db-4f586b2de7a7"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Normalized confusion matrix\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 432x288 with 0 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import itertools\n",
    "\n",
    "## display confusion matrix\n",
    "display_labels = ['N', 'S', 'V', 'F', 'Q']\n",
    "\n",
    "def plot_confusion_matrix(cm, classes,\n",
    "                          normalize=False,\n",
    "                          title='Confusion matrix',\n",
    "                          cmap=plt.cm.Blues):\n",
    "    \"\"\"\n",
    "    This function prints and plots the confusion matrix.\n",
    "    Normalization can be applied by setting `normalize=True`.\n",
    "    \"\"\"\n",
    "    if normalize:\n",
    "        cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]\n",
    "        print(\"Normalized confusion matrix\")\n",
    "    else:\n",
    "        print('Confusion matrix, without normalization')\n",
    "\n",
    "    plt.imshow(cm, interpolation='nearest', cmap=cmap)\n",
    "    plt.title(title)\n",
    "    plt.colorbar()\n",
    "    tick_marks = np.arange(len(classes))\n",
    "    plt.xticks(tick_marks, classes, rotation=45)\n",
    "    plt.yticks(tick_marks, classes)\n",
    "\n",
    "    fmt = '.2f' if normalize else 'd'\n",
    "    thresh = cm.max() / 2.\n",
    "    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):\n",
    "        plt.text(j, i, format(cm[i, j], fmt),\n",
    "                 horizontalalignment=\"center\",\n",
    "                 color=\"white\" if cm[i, j] > thresh else \"black\")\n",
    "\n",
    "    plt.tight_layout()\n",
    "    plt.ylabel('True label')\n",
    "    plt.xlabel('Predicted label')\n",
    "    plt.show()\n",
    "    plt.clf()\n",
    "    \n",
    "plot_confusion_matrix(c_matrix, display_labels ,\n",
    "                      title='Normalzied Confusion Matrix', normalize=True, cmap='Greys')"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "name": "ECG MIT Hyperparameter search.ipynb",
   "provenance": []
  },
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
